{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "github地址:[https://github.com/cheesezh/python_design_patterns](https://github.com/cheesezh/python_design_patterns)\n",
    "\n",
    "## 题目\n",
    "用代码模拟一天的工作状态，上午状态好，中午想睡觉，下午渐恢复，加班苦煎熬。\n",
    "\n",
    "## 基础版本——函数版"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前时间: 9 点, 上午工作，精神百倍\n",
      "当前时间: 10 点, 上午工作，精神百倍\n",
      "当前时间: 12 点, 饿了，午饭，犯困，午休\n",
      "当前时间: 13 点, 下午状态还可以，继续努力\n",
      "当前时间: 14 点, 下午状态还可以，继续努力\n",
      "当前时间: 17 点, 收工，下班\n",
      "当前时间: 19 点, 收工，下班\n",
      "当前时间: 22 点, 收工，下班\n"
     ]
    }
   ],
   "source": [
    "hour = 0\n",
    "work_finished = False\n",
    "\n",
    "def write_program():\n",
    "    if hour < 12:\n",
    "        print(\"当前时间: {} 点, 上午工作，精神百倍\".format(hour))\n",
    "    elif hour < 13:\n",
    "        print(\"当前时间: {} 点, 饿了，午饭，犯困，午休\".format(hour))\n",
    "    elif hour < 17:\n",
    "        print(\"当前时间: {} 点, 下午状态还可以，继续努力\".format(hour))\n",
    "    elif work_finished == True:\n",
    "        print(\"当前时间: {} 点, 收工，下班\".format(hour))\n",
    "    elif hour < 21:\n",
    "        print(\"当前时间: {} 点, 加班中，好累\".format(hour))\n",
    "    else:\n",
    "        print(\"当前时间: {} 点, 不行了，睡着了\".format(hour))\n",
    "        \n",
    "hour = 9\n",
    "write_program()\n",
    "\n",
    "hour = 10\n",
    "write_program()\n",
    "\n",
    "hour = 12\n",
    "write_program()\n",
    "\n",
    "hour = 13\n",
    "write_program()\n",
    "\n",
    "hour = 14\n",
    "write_program()\n",
    "\n",
    "hour = 17\n",
    "work_finished = True\n",
    "# work_finished = False\n",
    "write_program()\n",
    "\n",
    "hour = 19\n",
    "write_program()\n",
    "\n",
    "hour = 22\n",
    "write_program()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 改进版本1.0——初步封装"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前时间: 9 点, 上午工作，精神百倍\n",
      "当前时间: 10 点, 上午工作，精神百倍\n",
      "当前时间: 12 点, 饿了，午饭，犯困，午休\n",
      "当前时间: 13 点, 下午状态还可以，继续努力\n",
      "当前时间: 14 点, 下午状态还可以，继续努力\n",
      "当前时间: 17 点, 收工，下班\n",
      "当前时间: 19 点, 收工，下班\n",
      "当前时间: 22 点, 收工，下班\n"
     ]
    }
   ],
   "source": [
    "class Work():\n",
    "    def __init__(self):\n",
    "        self.hour = 0\n",
    "        self.task_finished = False\n",
    "        \n",
    "    def write_program(self):\n",
    "        if self.hour < 12:\n",
    "            print(\"当前时间: {} 点, 上午工作，精神百倍\".format(self.hour))\n",
    "        elif self.hour < 13:\n",
    "            print(\"当前时间: {} 点, 饿了，午饭，犯困，午休\".format(self.hour))\n",
    "        elif self.hour < 17:\n",
    "            print(\"当前时间: {} 点, 下午状态还可以，继续努力\".format(self.hour))\n",
    "        elif self.work_finished == True:\n",
    "            print(\"当前时间: {} 点, 收工，下班\".format(self.hour))\n",
    "        elif self.hour < 21:\n",
    "            print(\"当前时间: {} 点, 加班中，好累\".format(self.hour))\n",
    "        else:\n",
    "            print(\"当前时间: {} 点, 不行了，睡着了\".format(self.hour))\n",
    "            \n",
    "work = Work()\n",
    "work.hour = 9\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 10\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 12\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 13\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 14\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 17\n",
    "work.work_finished = True\n",
    "# work_finished = False\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 19\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 22\n",
    "work.write_program()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "- 这个类中的write_program方法过长，而且有很多判断分支，意味着它的责任过大了。面向对象设计其实就是希望做到代码的责任分解。所以这个类违背了`单一职责原则`；\n",
    "- 此外，write_program方法里有这么多判断，使得任何需求的改动或增加，都需要去更改这个方法。所以这个类也违背了`开放-封闭原则`；\n",
    "\n",
    "## 状态模式\n",
    "\n",
    "状态模式，当一个对象的内在状态改变是允许改变其行为，这个对象看起来像是改变了其类。[DP]\n",
    "\n",
    "状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中，可以把复杂的判断逻辑简化。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前状态: <class '__main__.StateB'>\n",
      "当前状态: <class '__main__.StateA'>\n",
      "当前状态: <class '__main__.StateB'>\n",
      "当前状态: <class '__main__.StateA'>\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class State():\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def handle(self, context):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class StateA(State):\n",
    "    \n",
    "    def handle(self, context):\n",
    "        context.set_state(StateB())\n",
    "    \n",
    "    \n",
    "class StateB(State):\n",
    "    \n",
    "    def handle(self, context):\n",
    "        context.set_state(StateA())\n",
    "        \n",
    "\n",
    "class Context():\n",
    "    \n",
    "    def __init__(self, state):\n",
    "        self.state = state\n",
    "        \n",
    "    def set_state(self, state):\n",
    "        self.state = state\n",
    "        print(\"当前状态: {}\".format(self.state.__class__))\n",
    "        \n",
    "    def request(self):\n",
    "        self.state.handle(self)  # 精髓\n",
    "        \n",
    "        \n",
    "def main():\n",
    "    context = Context(StateA())\n",
    "    context.request()\n",
    "    context.request()\n",
    "    context.request()\n",
    "    context.request()\n",
    "    \n",
    "main()  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 状态模式的好处与用处\n",
    "\n",
    "状态模式的好处是将与特定状态相关的行为局部化，并且将不同状态的行为分割开来。[DP]就是将特定的状态相关的行为都放入一个对象中，由于所有与状态相关的代码都存在于某个ConcretState中，所以通过定义新的子类可以很容易的增加新的状态和转换[DP]。这样做的目的就是为了消除庞大的条件分支语句，大的分支判断会使得它们难以修改和扩展。状态模式通过把各种状态转移逻辑分不到State的子类之间，来减少相互之间的依赖。\n",
    "\n",
    "什么时候需要考虑使用状态模式呢？当一个对象的行为取决于它的状态，并且它必须在运行时刻根据状态改变它的行为，就可以使用状态模式。另外，如果业务需求某项业务有多个状态，通常都是一些枚举常量，状态的变化都是依靠大量的分支判断语句来实现，此时应该考虑将每一种业务状态定义为一个State子类，这样这些对象就可以不依赖于其他对象而独立变化了，如果某天客户需求改了，增加或减少业务状态或改变状态流程，都不是困难了。\n",
    "\n",
    "## 改进版本2.0——状态模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前时间: 9 点, 上午工作，精神百倍\n",
      "当前时间: 10 点, 上午工作，精神百倍\n",
      "当前时间: 12 点, 饿了，午饭，犯困，午休\n",
      "当前时间: 13 点, 下午状态还可以，继续努力\n",
      "当前时间: 14 点, 下午状态还可以，继续努力\n",
      "当前时间: 17 点, 加班中，好累\n",
      "当前时间: 19 点, 加班中，好累\n",
      "当前时间: 22 点, 不行了，睡着了\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class State():\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def write_program(self, work):\n",
    "        pass\n",
    "    \n",
    "class ForenoonState(State):\n",
    "    \n",
    "    def write_program(self, work):\n",
    "        if work.hour < 12:\n",
    "            print(\"当前时间: {} 点, 上午工作，精神百倍\".format(work.hour))\n",
    "        else:\n",
    "            work.set_state(NoonState())\n",
    "            work.write_program()\n",
    "            \n",
    "    \n",
    "class NoonState(State):\n",
    "    \n",
    "    def write_program(self, work):\n",
    "        if work.hour < 13:\n",
    "            print(\"当前时间: {} 点, 饿了，午饭，犯困，午休\".format(work.hour))\n",
    "        else:\n",
    "            work.set_state(AfternoonState())\n",
    "            work.write_program()\n",
    "            \n",
    "            \n",
    "class AfternoonState(State):\n",
    "    \n",
    "    def write_program(self, work):\n",
    "        if work.hour < 17:\n",
    "            print(\"当前时间: {} 点, 下午状态还可以，继续努力\".format(work.hour))\n",
    "        else:\n",
    "            work.set_state(EveningState())\n",
    "            work.write_program()\n",
    "\n",
    "            \n",
    "class EveningState(State):\n",
    "    \n",
    "    def write_program(self, work):\n",
    "        if work.task_finished == True:\n",
    "            work.set_state(RestState())\n",
    "            work.write_program()\n",
    "        elif work.hour < 21:\n",
    "            print(\"当前时间: {} 点, 加班中，好累\".format(work.hour))\n",
    "        else:\n",
    "            work.set_state(SleepingState())\n",
    "            work.write_program()\n",
    "            \n",
    "\n",
    "class SleepingState(State):\n",
    "    \n",
    "    def write_program(self, work):\n",
    "        print(\"当前时间: {} 点, 不行了，睡着了\".format(work.hour))\n",
    "        \n",
    "        \n",
    "class RestState(State):\n",
    "    \n",
    "    def write_program(self, work):\n",
    "        print(\"当前时间: {} 点, 收工，下班\".format(work.hour))\n",
    "        \n",
    "        \n",
    "class Work():\n",
    "    \n",
    "    def __init__(self, state):\n",
    "        self.state = state\n",
    "        self.hour = 0\n",
    "        self.task_finished = False\n",
    "        \n",
    "    def set_state(self, state):\n",
    "        self.state = state\n",
    "        \n",
    "    def write_program(self):\n",
    "        self.state.write_program(self)  # 精髓\n",
    "        \n",
    "work = Work(ForenoonState())\n",
    "work.hour = 9\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 10\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 12\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 13\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 14\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 17\n",
    "work.work_finished = True\n",
    "# work_finished = False\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 19\n",
    "work.write_program()\n",
    "\n",
    "work.hour = 22\n",
    "work.write_program()\n",
    "            "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "假如老板规定“员工必须在20点之前离开公司”，那么只需要增加一个“强制下班状态”，然后改动一下“傍晚工作状态”就可以了。而这是不影响其他状态的代码的。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
